home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 8 / QRZ Ham Radio Callsign Database - Volume 8.iso / pc / files / t_unix / j109lxa4.tar / popserv.c < prev    next >
C/C++ Source or Header  |  1994-06-04  |  15KB  |  733 lines

  1. /* POP Server state machine - see RFC 937
  2.  *
  3.  *  also see other credits in popcli.c
  4.  *  10/89 Mike Stockett wa7dyx
  5.  *  Modified 5/27/90 by Allen Gwinn, N5CKP, for later NOS releases.
  6.  *  Added to NOS by PA0GRI 2/6/90 (and linted into "standard" C)
  7.  */
  8.  
  9. #include <stdio.h>
  10. #include <time.h>
  11. #ifdef UNIX
  12. #include <sys/types.h>
  13. #endif
  14. #include <sys/stat.h>
  15. #if     defined(__STDC__) || defined(__TURBOC__)
  16. #include <stdarg.h>
  17. #endif
  18. #include <ctype.h>
  19. #include <setjmp.h>
  20. #include "global.h"
  21. #include "mbuf.h"
  22. #include "cmdparse.h"
  23. #include "socket.h"
  24. #include "proc.h"
  25. #include "files.h"
  26. #include "pop.h"
  27. #include "dirutil.h"
  28.  
  29. #ifdef POP
  30.  
  31. extern char Nospace[];
  32.  
  33. /* Response messages */
  34.  
  35. static char    count_rsp[]    = "#%d messages in this folder\n",
  36.         error_rsp[]    = "- ERROR: %s\n",
  37.         greeting_msg[] = "+ POP2 %s\n",
  38. /*        length_rsp[]   = "=%ld bytes in this message\n", */
  39.         length_rsp[]   = "=%ld characters in Message #%d\n",
  40.         msg_line[]     = "%s\n",
  41.         no_mail_rsp[]  = "+ No mail, sorry\n",
  42.         no_more_rsp[]  = "=%d No more messages in this folder\n",
  43.         signoff_msg[]  = "+ Bye, thanks for calling\n";
  44.  
  45. static struct pop_scb *create_scb __ARGS((void));
  46. static void delete_scb __ARGS((struct pop_scb *scb));
  47. static void popserv __ARGS((int s,void *unused,void *p));
  48. static int poplogin __ARGS((char *pass,char *username));
  49.  
  50. /* I don't know why this isn't static, it isn't called anywhere else {was} */
  51. void pop_sm __ARGS((struct pop_scb *scb));
  52.  
  53. static int Spop = -1; /* prototype socket for service */
  54.  
  55. /* Start up POP receiver service */
  56. int
  57. pop1(argc,argv,p)
  58.  
  59. int argc;
  60. char *argv[];
  61. void *p;
  62.  
  63. {
  64.     struct sockaddr_in lsocket;
  65.     int s;
  66.  
  67.     if (Spop != -1) {
  68.         return 0;
  69.     }
  70.  
  71.     psignal(Curproc,0);             /* Don't keep the parser waiting */
  72.     chname(Curproc,"POP listener");
  73.  
  74.     lsocket.sin_family = AF_INET;
  75.     lsocket.sin_addr.s_addr = INADDR_ANY;
  76.     if(argc < 2)
  77.         lsocket.sin_port = IPPORT_POP;
  78.     else
  79.         lsocket.sin_port = atoi(argv[1]);
  80.  
  81.     Spop = socket(AF_INET,SOCK_STREAM,0);
  82.  
  83.     bind(Spop,(char *)&lsocket,sizeof(lsocket));
  84.  
  85.     listen(Spop,1);
  86.  
  87.     for (;;) {
  88.         if((s = accept(Spop,NULLCHAR,(int *)NULL)) == -1)
  89.             break;  /* Service is shutting down */
  90.  
  91.         /* Spawn a server */
  92.  
  93.         newproc("POP server",2048,popserv,s,NULL,NULL,0);
  94.     }
  95.     return 0;
  96. }
  97.  
  98. /* Shutdown POP service (existing connections are allowed to finish) */
  99.  
  100. int
  101. pop0(argc,argv,p)
  102. int argc;
  103. char *argv[];
  104. void *p;
  105.  
  106. {
  107.     close_s(Spop);
  108.     Spop = -1;
  109.     return 0;
  110. }
  111.  
  112. static void
  113. popserv(s,unused,p)
  114. int s;
  115. void *unused;
  116. void *p;
  117. {
  118.     struct pop_scb *scb;
  119.  
  120.     sockowner(s,Curproc);           /* We own it now */
  121.     log(s,"open POP");
  122.  
  123.     if((scb = create_scb()) == NULLSCB) {
  124.         tprintf(Nospace);
  125.         log(s,"close POP - no space");
  126.         close_s(s);
  127.         return;
  128.     }
  129.  
  130.     scb->socket = s;
  131.     scb->state  = AUTH;
  132.  
  133.     (void) usprintf(s,greeting_msg,Hostname);
  134.  
  135. loop:   if ((scb->count = recvline(s,scb->buf,BUF_LEN)) == -1){
  136.         /* He closed on us */
  137.  
  138.         goto quit;
  139.     }
  140.  
  141.     rip(scb->buf);
  142.     if (strlen(scb->buf) == 0)              /* Ignore blank cmd lines */
  143.         goto loop;
  144.     pop_sm(scb);
  145.     if (scb->state == DONE)
  146.         goto quit;
  147.  
  148.     goto loop;
  149.  
  150. quit:
  151.     log(scb->socket,"close POP");
  152.     close_s(scb->socket);
  153.     delete_scb(scb);
  154. }
  155.  
  156.  
  157. /* Create control block, initialize */
  158.  
  159. static struct
  160. pop_scb *create_scb()
  161. {
  162.     register struct pop_scb *scb;
  163.  
  164.     if((scb = (struct pop_scb *)callocw(1,sizeof (struct pop_scb))) == NULLSCB)
  165.         return NULLSCB;
  166.  
  167.     scb->username[0] = '\0';
  168.     scb->msg_status = NULL;
  169.     scb->wf = NULL;
  170.  
  171.     scb->count = scb->folder_file_size = scb->msg_num = 0;
  172.  
  173.     scb->folder_modified = FALSE;
  174.     return scb;
  175. }
  176.  
  177.  
  178. /* Free resources, delete control block */
  179.  
  180. static void
  181. delete_scb(scb)
  182. register struct pop_scb *scb;
  183. {
  184.  
  185.     if (scb == NULLSCB)
  186.         return;
  187.     if (scb->wf != NULL)
  188.         fclose(scb->wf);
  189.     if (scb->msg_status  != NULL)
  190.         free((char *)scb->msg_status);
  191.  
  192.     free((char *)scb);
  193. }
  194.  
  195. /* replace terminating end of line marker(s) (\r and \n) with null */
  196. void
  197. rrip(s)
  198. register char *s;
  199. {
  200.     register char *cp;
  201.  
  202.     if((cp = strchr(s,'\r')) != NULLCHAR)
  203.         *cp = '\0';
  204.     if((cp = strchr(s,'\n')) != NULLCHAR)
  205.         *cp = '\0';
  206. }
  207.  
  208. /* --------------------- start of POP server code ------------------------ */
  209.  
  210. #define BITS_PER_WORD           16
  211.  
  212. #define isSOM(x)                ((strncmp(x,"From ",5) == 0))
  213.  
  214. /* Command string specifications */
  215.  
  216. static char     ackd_cmd[] = "ACKD",
  217.         acks_cmd[] = "ACKS",
  218. #ifdef POP_FOLDERS
  219.         fold_cmd[] = "FOLD ",
  220. #endif
  221.         login_cmd[] = "HELO ",
  222.         nack_cmd[] = "NACK",
  223.         quit_cmd[] = "QUIT",
  224.         read_cmd[] = "READ",
  225.         retr_cmd[] = "RETR";
  226.  
  227. void
  228. pop_sm(scb)
  229. struct pop_scb *scb;
  230. {
  231.     char password[40];
  232.     void state_error(struct pop_scb *,char *);
  233.     void open_folder(struct pop_scb *);
  234.     void do_cleanup(struct pop_scb *);
  235.     void read_message(struct pop_scb *);
  236.     void retrieve_message(struct pop_scb *);
  237.     void deletemsg(struct pop_scb *,int);
  238.     void get_message(struct pop_scb *,int);
  239.     void print_message_length(struct pop_scb *);
  240.     void close_folder(struct pop_scb *);
  241. #ifdef POP_FOLDERS
  242.     void select_folder(struct pop_scb *);
  243. #endif
  244.  
  245.     if (scb == NULLSCB)     /* be certain it is good -- wa6smn */
  246.         return;
  247.  
  248.     switch(scb->state) {
  249.     case AUTH:
  250.         if (strncmp(scb->buf,login_cmd,strlen(login_cmd)) == 0){
  251.             sscanf(scb->buf,"HELO %s%s",scb->username,password);
  252.  
  253.             if (!poplogin(scb->username,password)) {
  254.                 log(scb->socket,"POP access DENIED to %s",
  255.                         scb->username);
  256.                 state_error(scb,"Access DENIED!!");
  257.                 return;
  258.             }
  259.  
  260.             log(scb->socket,"POP access granted to %s",
  261.                     scb->username);
  262.             open_folder(scb);
  263.         } else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
  264.             do_cleanup(scb);
  265.         } else
  266.             state_error(scb,"(AUTH) Expected HELO or QUIT command");
  267.         break;
  268.  
  269.     case MBOX:
  270.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  271.             read_message(scb);
  272.  
  273. #ifdef POP_FOLDERS
  274.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  275.             select_folder(scb);
  276.  
  277. #endif
  278.  
  279.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0) {
  280.             do_cleanup(scb);
  281.         } else
  282.             state_error(scb,
  283. #ifdef POP_FOLDERS
  284.                     "(MBOX) Expected FOLD, READ, or QUIT command");
  285. #else
  286.                     "(MBOX) Expected READ or QUIT command");
  287. #endif
  288.         break;
  289.  
  290.     case ITEM:
  291.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  292.             read_message(scb);
  293.  
  294. #ifdef POP_FOLDERS
  295.  
  296.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  297.             select_folder(scb);
  298. #endif
  299.  
  300.         else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
  301.             retrieve_message(scb);
  302.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
  303.             do_cleanup(scb);
  304.         else
  305.             state_error(scb,
  306. #ifdef POP_FOLDERS
  307.                "(ITEM) Expected FOLD, READ, RETR, or QUIT command");
  308. #else
  309.                "(ITEM) Expected READ, RETR, or QUIT command");
  310. #endif
  311.         break;
  312.  
  313.     case NEXT:
  314.         if (strncmp(scb->buf,ackd_cmd,strlen(ackd_cmd)) == 0){
  315.                 /* ACKD processing */
  316.             deletemsg(scb,scb->msg_num);
  317.             scb->msg_num++;
  318.             get_message(scb,scb->msg_num);
  319.         } else if (strncmp(scb->buf,acks_cmd,strlen(acks_cmd)) == 0){
  320.                 /* ACKS processing */
  321.             scb->msg_num++;
  322.             get_message(scb,scb->msg_num);
  323.         } else if (strncmp(scb->buf,nack_cmd,strlen(nack_cmd)) == 0){
  324.                 /* NACK processing */
  325.             fseek(scb->wf,scb->curpos,SEEK_SET);
  326.         } else {
  327.             state_error(scb,"(NEXT) Expected ACKD, ACKS, or NACK command");
  328.             return;
  329.         }
  330.  
  331.         print_message_length(scb);
  332.         scb->state  = ITEM;
  333.         break;
  334.  
  335.     case DONE:
  336.         do_cleanup(scb);
  337.         break;
  338.  
  339.     default:
  340.         state_error(scb,"(TOP) State Error!!");
  341.         break;
  342.     }
  343. }
  344.  
  345. void
  346. do_cleanup(scb)
  347. struct pop_scb *scb;
  348. {
  349.     void close_folder(struct pop_scb *);
  350.  
  351.     close_folder(scb);
  352.     (void) usprintf(scb->socket,signoff_msg);
  353.     scb->state = DONE;
  354. }
  355.  
  356. void
  357. state_error(scb,msg)
  358. struct pop_scb *scb;
  359. char *msg;
  360. {
  361.     (void) usprintf(scb->socket,error_rsp,msg);
  362.     scb->state = DONE;
  363. }
  364.  
  365. #ifdef POP_FOLDERS
  366.  
  367. select_folder(scb)
  368. struct pop_scb  *scb;
  369. {
  370.     sscanf(scb->buf,"FOLD %s",scb->username);
  371.  
  372.     if (scb->wf != NULL)
  373.         close_folder(scb);
  374.  
  375.     open_folder(scb);
  376. }
  377.  
  378. #endif
  379.  
  380.  
  381. void
  382. close_folder(scb)
  383. struct pop_scb *scb;
  384. {
  385.     char folder_pathname[64];
  386.     char line[BUF_LEN];
  387.     FILE *fd;
  388.     int deleted = FALSE;
  389.     int msg_no = 0;
  390.     int newmail(struct pop_scb *);
  391.     int isdeleted(struct pop_scb *,int);
  392.  
  393.     if (scb->wf == NULL)
  394.         return;
  395.  
  396.     if (!scb->folder_modified) {
  397.         /* no need to re-write the folder if we have not modified it */
  398.  
  399.         fclose(scb->wf);
  400.         scb->wf = NULL;
  401.  
  402.         free((char *)scb->msg_status);
  403.         scb->msg_status = NULL;
  404.         return;
  405.     }
  406.  
  407.  
  408.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  409.  
  410.     if (newmail(scb)) {
  411.         /* copy new mail into the work file and save the
  412.            message count for later */
  413.  
  414.         if ((fd = fopen(folder_pathname,"r")) == NULL) {
  415.             state_error(scb,"Unable to add new mail to folder");
  416.             return;
  417.         }
  418.  
  419.         fseek(scb->wf,0,SEEK_END);
  420.         fseek(fd,scb->folder_file_size,SEEK_SET);
  421.         while (!feof(fd)) {
  422.             fgets(line,BUF_LEN,fd);
  423.             fputs(line,scb->wf);
  424.         }
  425.  
  426.         fclose(fd);
  427.     }
  428.  
  429.     /* now create the updated mail folder */
  430.  
  431.     if ((fd = fopen(folder_pathname,"w")) == NULL){
  432.         state_error(scb,"Unable to update mail folder");
  433.         return;
  434.     }
  435.  
  436.     rewind(scb->wf);
  437.     while (!feof(scb->wf)){
  438.         fgets(line,BUF_LEN,scb->wf);
  439.  
  440.         if (isSOM(line)){
  441.             msg_no++;
  442.             if (msg_no <= scb->folder_len)
  443.                 deleted = isdeleted(scb,msg_no);
  444.             else
  445.                 deleted = FALSE;
  446.         }
  447.  
  448.         if (deleted)
  449.             continue;
  450.  
  451.         fputs(line,fd);
  452.     }
  453.  
  454.     fclose(fd);
  455.  
  456.     /* trash the updated mail folder if it is empty */
  457.     if(fsize(folder_pathname) == 0L)
  458.         unlink(folder_pathname);
  459.  
  460.     fclose(scb->wf);
  461.     scb->wf = NULL;
  462.  
  463.     free((char *)scb->msg_status);
  464.     scb->msg_status = NULL;
  465. }
  466.  
  467. void
  468. open_folder(scb)
  469. struct pop_scb  *scb;
  470. {
  471.     char folder_pathname[64];
  472.     char line[BUF_LEN];
  473.     FILE *fd;
  474.     FILE *tmpfile();
  475.  
  476.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  477.     scb->folder_len       = 0;
  478.     if((scb->folder_file_size=fsize(folder_pathname)) <= 0L) {
  479.          (void) usprintf(scb->socket,no_mail_rsp);
  480.          return;
  481.     }
  482.  
  483.     if ((fd = fopen(folder_pathname,"r")) == NULL){
  484.         state_error(scb,"Unable to open mail folder");
  485.         return;
  486.     }
  487.  
  488.     if ((scb->wf = tmpfile()) == NULL) {
  489.         state_error(scb,"Unable to create work folder");
  490.         return;
  491.     }
  492.  
  493.     while(!feof(fd)) {
  494.         fgets(line,BUF_LEN,fd);
  495.  
  496.         /* scan for begining of a message */
  497.  
  498.         if (isSOM(line))
  499.             scb->folder_len++;
  500.  
  501.         /* now put  the line in the work file */
  502.  
  503.         fputs(line,scb->wf);
  504.     }
  505.  
  506.     fclose(fd);
  507.  
  508.     scb->msg_status_size = (scb->folder_len) / BITS_PER_WORD;
  509.  
  510.     if ((((scb->folder_len) % BITS_PER_WORD) != 0) ||
  511.         (scb->msg_status_size == 0))
  512.         scb->msg_status_size++;
  513.  
  514.     if ((scb->msg_status = (unsigned int *) callocw(scb->msg_status_size,
  515.                 sizeof(unsigned int))) == NULL) {
  516.         state_error(scb,"Unable to create message status array");
  517.         return;
  518.     }
  519.  
  520.     (void) usprintf(scb->socket,count_rsp,scb->folder_len);
  521.  
  522.     scb->state  = MBOX;
  523. }
  524.  
  525. void
  526. read_message(scb)
  527. struct pop_scb  *scb;
  528. {
  529.     void get_message(struct pop_scb *,int);
  530.     void print_message_length(struct pop_scb *);
  531.  
  532.     if (scb == NULLSCB)     /* check for null -- wa6smn */
  533.         return;
  534.     if (scb->buf[sizeof(read_cmd) - 1] == ' ')
  535.         scb->msg_num = atoi(&(scb->buf[sizeof(read_cmd) - 1]));
  536.     else
  537.         scb->msg_num++;
  538.  
  539.     get_message(scb,scb->msg_num);
  540.     print_message_length(scb);
  541.     scb->state  = ITEM;
  542. }
  543.  
  544. void
  545. retrieve_message(scb)
  546. struct pop_scb  *scb;
  547. {
  548.     char line[BUF_LEN];
  549.     long cnt;
  550.     void rrip(char *);
  551.  
  552.     if (scb == NULLSCB)     /* check for null -- wa6smn */
  553.         return;
  554.     if (scb->msg_len == 0) {
  555.         state_error(scb,"Attempt to access a DELETED message!");
  556.         return;
  557.     }
  558.  
  559.     cnt  = scb->msg_len;
  560.     while(!feof(scb->wf) && (cnt > 0)) {
  561.         fgets(line,BUF_LEN,scb->wf);
  562.         rrip(line);
  563.  
  564.         (void) usprintf(scb->socket,msg_line,line);
  565.         cnt -= (strlen(line)+2);        /* Compensate for CRLF */
  566.     }
  567.  
  568.     scb->state = NEXT;
  569. }
  570.  
  571. void
  572. get_message(scb,msg_no)
  573. struct pop_scb  *scb;
  574. int msg_no;
  575. {
  576.     char line[BUF_LEN];
  577.     long ftell();
  578.     void rrip(char *);
  579.  
  580.     if (scb == NULLSCB)     /* check for null -- wa6smn */
  581.         return;
  582.     scb->msg_len = 0;
  583.     if (msg_no > scb->folder_len) {
  584.         scb->curpos  = 0;
  585.         scb->nextpos = 0;
  586.         return;
  587.     } else {
  588.         /* find the message and its length */
  589.  
  590.         rewind(scb->wf);
  591.         while (!feof(scb->wf) && (msg_no > -1)) {
  592.             if (msg_no > 0)
  593.                 scb->curpos = ftell(scb->wf);
  594.             
  595.             fgets(line,BUF_LEN,scb->wf);
  596.             rrip(line);
  597.  
  598.             if (isSOM(line))
  599.                 msg_no--;
  600.  
  601.             if (msg_no != 0)
  602.                 continue;
  603.  
  604.             scb->nextpos  = ftell(scb->wf);
  605.             scb->msg_len += (strlen(line)+2);       /* Add CRLF */
  606.         }
  607.     }
  608.  
  609.     if (scb->msg_len > 0)
  610.         fseek(scb->wf,scb->curpos,SEEK_SET);
  611.  
  612.     /* we need the pointers even if the message was deleted */
  613.  
  614.     if  (isdeleted(scb,scb->msg_num))
  615.         scb->msg_len = 0;
  616. }
  617.  
  618. static int
  619. poplogin(username,pass)
  620. char *pass;
  621. char *username;
  622. {
  623.     char buf[80];
  624.     char *cp;
  625.     char *cp1;
  626.     FILE *fp;
  627.  
  628.     if((fp = fopen(Popusers,"r")) == NULLFILE) {
  629.         /* User file doesn't exist */
  630.         tprintf("POP users file %s not found\n",Popusers);
  631.         return(FALSE);
  632.     }
  633.  
  634.     while(fgets(buf,sizeof(buf),fp),!feof(fp)) {
  635.         if(buf[0] == '#')
  636.             continue;       /* Comment */
  637.  
  638.         if((cp = strchr(buf,':')) == NULLCHAR)
  639.             /* Bogus entry */
  640.             continue;
  641.  
  642.         *cp++ = '\0';           /* Now points to password */
  643.         if(strcmp(username,buf) == 0)
  644.             break;          /* Found user name */
  645.     }
  646.  
  647.     if(feof(fp)) {
  648.         /* User name not found in file */
  649.  
  650.         fclose(fp);
  651.         return(FALSE);
  652.     }
  653.     fclose(fp);
  654.  
  655.     if ((cp1 = strchr(cp,':')) == NULLCHAR)
  656.         return(FALSE);
  657.  
  658.     *cp1 = '\0';
  659.     if(strcmp(cp,pass) != 0) {
  660.         /* Password required, but wrong one given */
  661.  
  662.         return(FALSE);
  663.     }
  664.  
  665.     /* whew! finally made it!! */
  666.  
  667.     return(TRUE);
  668. }
  669.  
  670. int
  671. isdeleted(scb,msg_no)
  672. struct pop_scb *scb;
  673. int msg_no;
  674. {
  675.     unsigned int mask = 1,offset;
  676.  
  677.     msg_no--;
  678.     offset = msg_no / BITS_PER_WORD;
  679.     mask <<= msg_no % BITS_PER_WORD;
  680.     return (((scb->msg_status[offset]) & mask)? TRUE:FALSE);
  681. }
  682.  
  683. void
  684. deletemsg(scb,msg_no)
  685. struct pop_scb *scb;
  686. int msg_no;
  687. {
  688.     unsigned int mask = 1,offset;
  689.  
  690.     if (scb == NULLSCB)     /* check for null -- wa6smn */
  691.         return;
  692.     msg_no--;
  693.     offset = msg_no / BITS_PER_WORD;
  694.     mask <<= msg_no % BITS_PER_WORD;
  695.     scb->msg_status[offset] |= mask;
  696.     scb->folder_modified = TRUE;
  697. }
  698.  
  699. int
  700. newmail(scb)
  701. struct pop_scb *scb;
  702. {
  703.     char folder_pathname[64];
  704.     long newsize;
  705.  
  706.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  707.  
  708.     if((newsize=fsize(folder_pathname)) < 0L) {
  709.         state_error(scb,"Unable to get old mail folder's status");
  710.         return(FALSE);
  711.     } else
  712.     return ((newsize > scb->folder_file_size)? TRUE:FALSE);
  713. }
  714.  
  715. void
  716. print_message_length(scb)
  717. struct pop_scb *scb;
  718. {
  719.     char *print_control_string;
  720.  
  721.     if (scb == NULLSCB)     /* check for null -- wa6smn */
  722.         return;
  723.     if (scb->msg_len > 0)
  724.         print_control_string = length_rsp;
  725.     else if (scb->msg_num <= scb->folder_len)
  726.         print_control_string = length_rsp;
  727.     else
  728.         print_control_string = no_more_rsp;
  729.  
  730.     (void)usprintf(scb->socket,print_control_string,scb->msg_len,scb->msg_num);
  731. }
  732. #endif
  733.